The Data Object Wizard is an add-in that can help you to quickly generate data-aware class and UserControl modules. This wizard is probably the most sophisticated add-in provided with Visual Basic 6. Unfortunately, it's also one of the least intuitive to use. In the remainder of this chapter, I'll briefly introduce this utility. (Space doesn't permit me to explain all its capabilities in depth.)
The Data Object Wizard works in conjunction with the DataEnvironment designer. Instead of entering all the necessary information about the data source when the Data Object Wizard is executing, you have to prepare a DataEnvironment object with one or more Command objects before running the add-in. Each Command object represents one of the actions that you can perform on the data source: select, insert, update, or delete records, lookup values, and so on. Once you run the wizard, you can't go back to the Visual Basic IDE, so you need to prepare all the Command objects in advance.
In this section, I guide you through a simple example based on the Products table of the NWind.mdb database. Frankly, the Data Object Wizard works best when used on SQL Server and Oracle databases. Even so, I opted for an example based on a local MDB database for those of you who don't have a client/server system available. These are the preparatory steps that you have to follow before running the wizard:
INSERT INTO Products(ProductName, CategoryID, SupplierID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) |
Figure 18-6. The Properties dialog box of the Products_Insert Command object.
UPDATE Products SET ProductName = ?, CategoryID = ?, SupplierID = ?, QuantityPerUnit = ?, UnitPrice = ?, UnitsInStock = ?, UnitsOnOrder = ?, ReorderLevel = ?, Discontinued = ? WHERE (ProductID = ?) |
Then switch to the Parameters tab and assign meaningful names and types for all the parameters, exactly as you did in step 5. (Unfortunately, there's no way to copy and paste this information between Command objects.)
DELETE FROM Products WHERE ProductID = ? |
Then switch to the Parameters tab and assign the ProductID name to the only parameter, without changing the other attributes.
All these steps shouldn't take more than 5 or 10 minutes. If you prefer, you can start with a blank Standard EXE project and then just load the DE1.dsr file from the project on the companion CD. This file already contains all the Command objects ready to be used by the Data Object Wizard.
Most of the time required to build these Command objects is spent manually entering the names and other attributes of the many parameters in the Products_Insert and Products_Update commands. You can't avoid this step when working with MDB databases because the OLE DB provider doesn't correctly recognize any parameterized QueryDef object stored in the database. The good news is that when you work with SQL Server you can create Command objects that link to stored procedures. In this case, the DataEnvironment designer is able to deduce the name and type of parameters without your help, which dramatically reduces the amount of time necessary to complete these preparatory steps.
You're now ready to run the Data Object Wizard. If you haven't loaded it yet, select the Add-In Manager command from the Add-Ins menu, double-click on its name in the list of available add-ins, and click on the OK button. Now the wizard should be available in the Add-Ins menu. Run it, and then follow these steps:
Repeat the same four operations to define CategoryID as another lookup field, as shown in Figure 18-7.
Figure 18-7. The Define Lookup Table Information page, after you've added the SupplierID field to the list of lookup fields and just before you add the CategoryID field.
Figure 18-8. Mapping field names to parameter names is crucial when you define Command objects for inserting, updating, and deleting records, but in most cases the wizard is able to do the mapping automatically.
Going through the preceding steps might seem complex at first, but after some practice you'll see that using the wizard really takes no more than a couple of minutes. When the wizard completes its execution, you'll find that two new classes have been added to the current project: the clsDow class and the rsclsProducts class. The clsDow class module contains only the EnumSaveMode enumerated constants, which define the values that can be assigned to the SaveMode property of the rsclsProducts class: 0-adImmediate, if you want the class to save values in the source Recordset as soon as the record pointer moves to another record, or 1-adBatch, if the class should update the Recordset only when you invoke the class's Update method.
You could use the rsclsProducts class module created by the wizard directly from your applications, but you'll find it more convenient to use it through a custom UserControl. The great news is that you can create such a UserControl in a matter of seconds, again using the Data Object Wizard.
Figure 18-9. The Map Class Properties To A Control Type page of the DataObject Wizard.
You're now ready to use the control in the application. Close the UserControl module so that its icon in the Toolbox becomes active, create an instance of the control on a form, and add a few navigational buttons, as shown in Figure 18-10. The code behind these buttons is really simple:
Private Sub cmdPrevious_Click() uctProductsSingleRecord1.MovePrevious End Sub Private Sub cmdNext_Click() uctProductsSingleRecord1.MoveNext End Sub Private Sub cmdAddNew_Click() uctProductsSingleRecord1.AddRecord End Sub Private Sub cmdUpdate_Click() uctProductsSingleRecord1.Update End Sub Private Sub cmdDelete_Click() uctProductsSingleRecord1.Delete End Sub |
Figure 18-10. The ActiveX control generated by the Data Object Wizard can be tested with a few navigational buttons on its parent form.
The Data Object Wizard isn't particularly efficient when used with the OLE DB Provider for Microsoft Jet. After some experiments, I found that if you want to add new records, you must set the UserControl's SaveMode property to 1-adBatch, and therefore you have to invoke the Update method after entering a new record. Everything works unproblematically when you create classes and UserControls that bind to a SQL Server database.
When you understand the mechanism, creating the other types of UserControls is easy. For example, restart the wizard and create a DataGrid-like control. If you then place the control on a form and set its GridEditable property to True, you'll see that not only can you edit field values in the grid but you can also select the value of a lookup field from a drop-down list, as shown in Figure 18-11. The DataList-like and DataCombo-like controls are even simpler because they're just lists of values and don't use the Insert, Update, Delete, and Lookup Command objects.
You can create more flexible classes and controls if the original source Command object is based on a parameterized query or a stored procedure, such as this one:
SELECT * FROM Products WHERE ProductName LIKE ? |
In this case, the resulting class and UserControl modules expose a property whose name is obtained by concatenating the name of the source Command and the name of the parameter in the query (for example, Products_ProductName). You can set this property at design time and let the UserControl initialize the internal Recordset as soon as the control is created at run time. Or you can set the ManualInitialize property to True so that you can assign this property using code and then manually invoke the Initxxxx method exposed by the control (InitProducts in this example). The sample application shown in Figure 18-11 uses this technique to narrow the number of records displayed in the grid. This is the only code in the form module:
Private Sub cmdFetch_Click() uctProductsDataGrid1.Products_FetchProductName = txtProductName & "%" uctProductsDataGrid1.InitProducts2 End Sub |
The Data Object Wizard is a great add-in, and it produces very good code. In fact, I suggest that you study the generated code to learn how to get the maximum benefit from this utility and also to learn new tricks for building better data-aware classes and UserControls. The wizard also has some defects, however. Apart from those that I've already mentioned (and that are mostly caused by bugs in the OLE DB Provider for Microsoft Jet), the one that bothers me most is that the UserControl module tends to go out of sync with its instances on forms, so you often need to right-click on forms and invoke the Update UserControls menu command. This is a minor nuisance, however, when compared to the time the wizard saves.
Figure 18-11. DataGrid-like UserControls also let you select values from drop-down lists.
In this chapter, you've seen that ADO permits you to build many new types of classes and components: data consumers, data sources, and OLE DB Simple Providers. You can build another type of database component with Visual Basic, a Remote Data Services (RDS) component. You normally use these when you're accessing a database through the HTTP protocol, and for this reason I'll describe this type of component in the next chapter, together with the new Visual Basic features in the Internet area.